<template>
  <view class="uv-skeleton">
    <view v-if="loading">
      <view v-for="(item, index) in elements" :key="index">
        <!-- 横向并列布局 -->
        <view class="uv-skeleton__group" :style="[style(item)]" v-if="item.type == 'flex'">
          <view v-for="(item2, index2) in item.children" :class="[item2.clas]" :style="[style(item2)]" :key="index2">
            <view v-for="(item3, index3) in item2.elements" :key="index3">
              <!-- 垂直高度站位 -->
              <view :style="{ height: $uv.addUnit(item3.height, 'rpx') }" v-if="item3.type == 'gap'"></view>
              <view
                :class="[item3.clas, roundClass, animateClass]"
                :style="[style(item3)]"
                v-else
                ref="skeleton"
              ></view>
            </view>
          </view>
        </view>
        <!-- 自定义骨架屏内容 -->
        <!-- ps 自定义扩展说明： -->
        <!-- 如果你需要自定义模板，可以参照这个custom的写法，增加一个skeleton配置类型，编写布局和样式就可以了 -->
        <!-- 注意事项：为了保证骨架效果和动态效果的一致，扩展时，在你希望实际展示在页面中的元素上加上 :class="[style, animateClass]" 和 :style="[style(item)]" 和 ref="skeleton" -->
        <view
          :class="[item.clas, animateClass]"
          :style="[style(item)]"
          ref="skeleton"
          v-else-if="item.type == 'custom'"
        ></view>
        <!-- 垂直高度站位 -->
        <view :style="{ height: $uv.addUnit(item.height, 'rpx') }" v-else-if="item.type == 'gap'"></view>
        <!-- 其他基本单位 line avatar 等 -->
        <view :class="[item.clas, roundClass, animateClass]" :style="[style(item)]" v-else ref="skeleton"></view>
      </view>
    </view>
    <view v-else>
      <slot />
    </view>
  </view>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
// #ifdef APP-NVUE
const animation = weex.requireModule('animation')
// #endif
export default {
  name: 'uv-skeletons',
  mixins: [mpMixin, mixin],
  props: {
    // 是否显示骨架
    loading: {
      type: Boolean,
      default: true,
    },
    // 骨架内容 具体说明参考文档：
    skeleton: {
      type: Array,
      default: () => [],
    },
    // 是否开启动画效果
    animate: {
      type: Boolean,
      default: true,
    },
    // 是否圆角骨架风格
    round: {
      type: Boolean,
      default: false,
    },
    ...uni.$uv?.props?.skeleton,
  },
  data() {
    return {
      elements: [],
      opacity: 0.5,
      duration: 600,
    }
  },
  computed: {
    animateClass() {
      return this.animate ? 'uv-skeleton--animation' : 'uv-skeleton--static'
    },
    roundClass() {
      return this.round ? 'uv-skeleton--round' : 'uv-skeleton--radius'
    },
    style(item) {
      return (item) => {
        const style = {}
        return this.$uv.deepMerge(style, this.$uv.addStyle(item.style))
      }
    },
  },
  created() {
    this.init()
    // #ifdef APP-NVUE
    if (this.loading && this.animate) {
      this.$uv.sleep(50).then((res) => {
        this.createAnimation(this.opacity)
      })
    }
    // #endif
  },
  methods: {
    /**
     * 初始化数据
     */
    init() {
      let elements = []
      if (!this.$uv.test.array(this.skeleton)) return this.$uv.error('skeleton参数必须为数组形式，参考文档示例：')
      this.skeleton.forEach((el) => {
        const elClass = this.getElCounts(el)
        elements = elements.concat(elClass)
      })
      this.elements = [...elements]
    },
    /**
     * 处理骨架屏参数内容
     * @param {Object} el 每项数据
     */
    getElCounts(el) {
      let elements = []
      let children = []
      if (this.$uv.test.number(el)) {
        elements.push({
          type: 'gap',
          height: el,
        })
        return elements
      } else {
        const type = el.type ? el.type : 'line'
        const num = el.num ? el.num : 1
        const style = el.style ? el.style : {}
        const styleIsArray = this.$uv.test.array(style)
        const gap = el.gap ? el.gap : '20rpx'
        const child = el.children ? el.children : []
        for (let i = 0; i < child.length; i++) {
          children[i] = {
            clas:
              child[i].type.indexOf('avatar') > -1 || child[i].type.indexOf('custom') > -1
                ? ''
                : 'uv-skeleton__group__sub',
            elements: this.getElCounts(child[i]),
          }
        }
        for (let i = 0; i < num; i++) {
          if (gap && i < num && i > 0) {
            elements.push({
              type: 'gap',
              height: gap,
            })
          }
          elements.push({
            clas: `uv-skeleton__${type}`,
            type,
            style: styleIsArray ? style[i] : style,
            gap,
            children,
          })
        }
        return elements
      }
    },
    /**
     * 创建动画
     */
    createAnimation(opacity = 1) {
      // loading结束之后或未开启动画不执行
      if (!this.loading || !this.animate) return
      let count = 0
      const skeletons = this.$refs.skeleton
      skeletons.forEach((item) => {
        animation.transition(
          item,
          {
            styles: {
              opacity: opacity,
            },
            duration: this.duration,
          },
          () => {
            count++
            if (count >= skeletons.length) {
              setTimeout(() => {
                this.createAnimation(opacity < 1 ? 1 : this.opacity)
              }, 200)
            }
          },
        )
      })
    },
  },
}
</script>
<style scoped lang="scss">
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
@mixin background {
  /* #ifdef APP-NVUE */
  background-color: #e6e6e6;
  /* #endif */
  /* #ifndef APP-NVUE */
  background: linear-gradient(90deg, #f1f2f4 25%, #e6e6e6 37%, #f1f2f4 50%);
  background-size: 400% 100%;
  /* #endif */
}
.uv-skeleton__line {
  height: 32rpx;
  // margin-bottom: 30rpx;
}
.uv-skeleton__line--sm {
  height: 24rpx;
  margin-bottom: 30rpx;
}
.uv-skeleton__line--lg {
  height: 48rpx;
  margin-bottom: 30rpx;
}
.uv-skeleton--static {
  @include background;
}
.uv-skeleton--radius {
  border-radius: 8rpx;
}
.uv-skeleton--round {
  border-radius: 30rpx;
}
.uv-skeleton__avatar {
  width: 100rpx;
  height: 100rpx;
  border-radius: 50rpx;
}
.uv-skeleton__avatar--sm {
  width: 50rpx;
  height: 50rpx;
  border-radius: 25rpx;
}
.uv-skeleton__avatar--lg {
  width: 200rpx;
  height: 200rpx;
  border-radius: 100rpx;
}
.uv-skeleton__group {
  @include flex;
  &__sub {
    flex: 1;
  }
}
.uv-skeleton--animation {
  @include background;
  /* #ifndef APP-NVUE */
  animation: skeleton 1.8s ease infinite;
  /* #endif */
}
/* #ifndef APP-NVUE */
@keyframes skeleton {
  0% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0 50%;
  }
}
/* #endif */
</style>
